package com.vhall.component.service.vote.delay;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.vote.RoomVoteLkMapper;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.vote.RoomVoteLkEntity;
import com.vhall.component.entity.vote.vo.VoteDetailReqVO;
import com.vhall.component.entity.vote.vo.VoteDetailRspVO;
import com.vhall.component.entity.vote.vo.room.FinishReqVO;
import com.vhall.component.entity.vote.vo.room.PushReqVO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.PaasCommonConstant;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.constant.RoomConstant;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.support.VssTokenThreadLocal;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.vote.VoteConstants;
import com.vhall.component.service.vote.VoteRoomService;
import com.vhall.component.service.vote.VoteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author jingwen.li
 */
@Slf4j
@Component
public class DelayTaskRunner implements InitializingBean {
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private DelayQueue<DelayEvent> voteDelayQueue;
    @Autowired
    private VoteRoomService voteRoomService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RoomVoteLkMapper roomVoteLkMapper;
    @Autowired
    private VoteService voteService;
    @Autowired
    private RoomInnerService roomInnerService;
    @Autowired
    private PaasService paasService;


    @Override
    public void afterPropertiesSet() throws Exception {
        getCacheDelayCache();
        threadPoolTaskExecutor.execute(() -> {
            while (true) {
                try {
                    DelayEvent event = voteDelayQueue.poll();
                    if (event == null) {
                        Thread.sleep(1000);
                        continue;
                    }
                    if (Boolean.FALSE.equals(stringRedisTemplate.opsForValue().setIfAbsent(RedisKey.VOTE_DELAY_ID + event.getVoteId(), "1", 1, TimeUnit.MINUTES))) {
                        continue;
                    }
                    log.info("房间:{}的投票:{},倒计时结束,公布投票结果", event.getRoomId(), event.getVoteId());
                    voteFinish(event.getAccountId(), event.getVoteId(), event.getRoomId());
                    Thread.sleep(1000);
                    pushMsg(event.getAccountId(), event.getVoteId(), event.getRoomId());

                } catch (Exception e) {
                    log.error("自动公布投票结果发生异常:{}", e);
                }
            }
        });
    }

    private void getCacheDelayCache() {
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisKey.VOTE_DELAY_QUEUE))) {
            List<String> range = stringRedisTemplate.opsForList().range(RedisKey.VOTE_DELAY_QUEUE, 0, -1);
            if (CollUtil.isEmpty(range)) {
                return;
            }
            range.forEach(s -> {
                DelayEvent event = JSON.parseObject(s, DelayEvent.class);
                voteDelayQueue.add(event);
            });

        }
    }

    private Boolean voteFinish(Integer accountId, Long voteId, String roomId) {
        log.info("推送投票结束消息--start");
        RoomJoinsEntityDTO roomJoinsEntityDTO = checkRoomRoleName(accountId, roomId);

        RoomVoteLkEntity roomVoteLkEntity = roomVoteLkMapper.selectOne(Wrappers.<RoomVoteLkEntity>lambdaQuery()
                .eq(RoomVoteLkEntity::getRoomId, roomId)
                .eq(RoomVoteLkEntity::getVoteId, voteId));
        if (roomVoteLkEntity == null) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_INVALID);
        }
        if (roomVoteLkEntity.getIsFinish() == VoteConstants.RoomVoteLkBind.FINISH) {
            log.info("推送投票结束消息--投票已结束, voteId={}", voteId);
            return true;
        }
        roomVoteLkEntity.setIsFinish(VoteConstants.RoomVoteLkBind.FINISH);
        roomVoteLkEntity.setFinishTime(LocalDateTime.now());
        roomVoteLkEntity.setUpdatedAt(LocalDateTime.now());
        roomVoteLkMapper.updateById(roomVoteLkEntity);

        //发送结束
        Map<String, Object> body = new HashMap<>(16);
        body.put("type", "vote_finish");
        body.put(PaasCommonConstant.VOTE_ID_KEY, voteId);
        body.put(PaasCommonConstant.NICK_NAME_KEY, roomJoinsEntityDTO.getNickname());
        body.put(PaasCommonConstant.ROOM_JOIN_ID_KEY, roomJoinsEntityDTO.getAccountId());
        body.put(PaasCommonConstant.ROOM_ROLE_KEY, roomJoinsEntityDTO.getRoleName());
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);

        paasService.sendMessage(roomInfo.getChannelId(), JSON.toJSONString(body));

        //发送公告
//        noticesService.sendNotice(roomId, voteId + "", roomJoinsEntityDTO.getAccountId(), "vote_finish");

        // 删除缓存的投票 ID
        delRunningVoteIdCache(roomId, voteId);
        log.info("推送投票结束消息--end");
        return true;
    }

    private void delRunningVoteIdCache(String roomId, Long voteId) {
        String lua = " local runVoteId = redis.call('hget', KEYS[1], ARGV[1])\n" +
                "        if runVoteId == ARGV[2] then\n" +
                "            redis.call('hdel', KEYS[1], ARGV[1])\n" +
                "        end\n" +
                "        return runVoteId";
        stringRedisTemplate.execute((RedisCallback) connection -> {
            connection.eval(lua.getBytes(StandardCharsets.UTF_8), ReturnType.VALUE, 1,
                    (RedisKey.INTERACT_TOOL + roomId).getBytes(StandardCharsets.UTF_8),
                    RedisKey.INTERACT_TOOL_FILED.getBytes(StandardCharsets.UTF_8),
                    String.valueOf(voteId).getBytes(StandardCharsets.UTF_8));
            return null;
        });
    }

    private void pushMsg(Integer accountId, Long voteId, String roomId) {
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisKey.VOTE_PUSH_FLAG + voteId))) {
            stringRedisTemplate.delete(RedisKey.VOTE_PUSH_FLAG + voteId);
            return;
        }
        log.info("推送投票结果消息--start");
        if (stringRedisTemplate.opsForList().size(RedisKey.VOTE_ANSWER_LIST_QUEUE + roomId) > 0) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_BUSINESS_RESULT_STATING);
        }

        RoomJoinsEntityDTO roomJoinsEntityDTO = checkRoomRoleName(accountId, roomId);

        RoomVoteLkEntity roomVoteLkEntity = roomVoteLkMapper.selectOne(Wrappers.<RoomVoteLkEntity>lambdaQuery()
                .eq(RoomVoteLkEntity::getRoomId, roomId)
                .eq(RoomVoteLkEntity::getVoteId, voteId));
        if (roomVoteLkEntity == null) {
            throw new BusinessException(BizErrorCode.COMP_VOTE_INVALID);
        }
        if (roomVoteLkEntity.getPublish() != VoteConstants.RoomVoteLkBind.PUBLISH_YES) {
            log.info("推送投票结果消息--投票未发布,voteId={}", voteId);
            return;
        }
        if (roomVoteLkEntity.getIsFinish() != VoteConstants.RoomVoteLkBind.FINISH) {
            log.info("推送投票结果消息--投票未结束,voteId={}", voteId);
            return;
        }
        roomVoteLkEntity.setIsRelease(VoteConstants.RoomVoteLkBind.RELEASE);
        roomVoteLkMapper.updateById(roomVoteLkEntity);
        VoteDetailReqVO voteDetailReqVO = new VoteDetailReqVO();
        voteDetailReqVO.setVoteId(Long.valueOf(voteId));
        voteDetailReqVO.setRoomId(roomId);
        voteDetailReqVO.setAccountId(accountId);
        voteDetailReqVO.setForm("1");
        List<VoteDetailRspVO> voteDetailRspVO = voteService.voteDetail(voteDetailReqVO, accountId);

        Map<String, Object> body = new HashMap<>(8);
        body.put("type", "vote_final_statis");
        body.put(PaasCommonConstant.VOTE_ID_KEY, voteId);
        body.put(PaasCommonConstant.NICK_NAME_KEY, roomJoinsEntityDTO.getNickname());
        body.put(PaasCommonConstant.ROOM_JOIN_ID_KEY, roomJoinsEntityDTO.getAccountId());
        body.put(PaasCommonConstant.ROOM_ROLE_KEY, roomJoinsEntityDTO.getRoleName());
        body.put("vote_static", voteDetailRspVO);
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
        log.info("推送投票结果消息--end,房间:{},vote:{}", roomId, voteId);
        paasService.sendMessage(roomInfo.getChannelId(), JSON.toJSONString(body));
    }

    private RoomJoinsEntityDTO checkRoomRoleName(Integer accountId, String roomId) {
        RoomJoinsEntityDTO roomJoinsEntityDTO = roomInnerService.getRoomJoins(accountId, roomId);
        if (roomJoinsEntityDTO == null
                || roomJoinsEntityDTO.getRoleName().equals(RoomConstant.AUDIENCE)) {
            throw new BusinessException(BizErrorCode.BIZ_NO_PERMISSION);
        }
        return roomJoinsEntityDTO;
    }
}
